package com.frontop.terminal.downloader;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.frontop.terminal.component.ResourcePublishing;
import com.frontop.terminal.constant.DownloadConst;
import com.frontop.terminal.constant.InterfaceConst;
import com.frontop.terminal.entity.AwaitInform;
import com.frontop.terminal.entity.FilePart;
import com.frontop.terminal.entity.TerminalConfig;
import com.frontop.terminal.enums.HeaderEnum;
import com.frontop.terminal.util.ReadDataUtil;
import com.frontop.terminal.util.SystemMonitoringUtil;
import com.frontop.terminal.util.WriteDataUtil;
import com.frontop.terminal.vlcjplayer.VlcjPlayer;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

/**
 * @author CCC
 * 下载任务
 */
@SuppressWarnings({"ResultOfMethodCallIgnored", "ConstantConditions", "rawtypes"})
@Data
@Slf4j
public class MultipleThreadDownloadTask implements Runnable {

    /**
     * 线程池
     * 1. 初始线程数为corePoolSize指定的大小
     * 2. 没有最大线程数限制
     * 3. 默认使用LinkedBlockingQueue，默认队列大小为1024
     * 4. 当运行线程大于corePoolSize放入队列，队列满后抛出异常
     */
    private ExecutorService executorServiceConcurrence;

    private String uri;
    private File target;
    private Long residue;
    private Long fileId;
    private Integer type;

    public MultipleThreadDownloadTask(String uri, File target, Long fileId, Integer type) {
        this.target = target;
        this.uri = uri;
        this.fileId = fileId;
        this.type = type;
        executorServiceConcurrence = ThreadUtil.newExecutor(10);
    }

    public static void savePartTas(List<FilePart> fileParts) {
        String json = JSON.toJSONString(fileParts);
        String path = fileParts.get(0).getFilePath() + ".tmp";
        File file = new File(path);
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        WriteDataUtil.write(json, path);
    }

    /**
     * 下载资源成功通知服务器更新状态
     *
     * @param mediaId 媒体id
     */
    public static void updateMediaLinkState(Long mediaId) {
        String url = ReadDataUtil.getTerminalConfig().getServerAddress() + InterfaceConst.DOWNLOAD_RESOURCE_SUCCESS;
        //用于服务器查询该终端数据
        String mac = SystemMonitoringUtil.getMacAddress();
        boolean requestStatus = false;
        try {
            requestStatus = HttpRequest.post(url)
                    //设置连接超时和响应超时时间
                    .timeout(3000)
                    .header(HeaderEnum.TERMINAL_TOKEN.getKey(), ReadDataUtil.getTerminalConfig().getAuthorizationCode())
                    .form("mac", mac)
                    .form("id", mediaId)
                    .execute()
                    .isOk();

        } catch (Exception e) {
            log.warn("通知服务器同步资源发布状态时请求失败,将稍后重试.\t媒体ID:" + mediaId);
        }

        //通知失败存放在待通知列表
        if (!requestStatus) {
            log.warn("通知服务器同步资源发布状态时请求失败,将稍后重试.\t媒体ID:" + mediaId);
            List<AwaitInform> awaitInformList = ReadDataUtil.getAwaitInform();
            AwaitInform awaitInform = new AwaitInform(mediaId, mac);

            if (Objects.isNull(awaitInformList)) {
                awaitInformList = new ArrayList<>();
            }

            awaitInformList.add(awaitInform);
            WriteDataUtil.write(awaitInformList, ReadDataUtil.AWAITINFORM_LIST_CODING);
        }
    }

    /**
     * 开始下载
     */
    public void start() {
        String json = ReadDataUtil.read(target.getPath() + ".tmp");
        //不为空接着下载未完成的分片
        if (unfinishedWork(json)) {
            return;
        }

        //获取文件总大小
        long totalSize;
        try {
            TerminalConfig terminalConfig = ReadDataUtil.getTerminalConfig();

            URL url = new URL(uri);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
            connection.setRequestProperty(HeaderEnum.TERMINAL_TOKEN.getKey(), terminalConfig.getAuthorizationCode());
            connection.connect();
            int httpCode = connection.getResponseCode();
            //请求状态异常
            if (httpCode != HttpServletResponse.SC_OK && httpCode != HttpServletResponse.SC_PARTIAL_CONTENT) {
                throw new Exception("下载资源过程中连接服务器异常,暂停本次下载任务");
            }
            totalSize = connection.getContentLengthLong();
            if (totalSize == -1) {
                throw new Exception("请求的资源长度异常,暂停本次下载任务");
            }
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }

        //如果文件不存在则创建
        if (!target.exists()) {
            try {
                target.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        log.info("开始下载文件，ID为:" + fileId);
        //更新文件状态为正在下载
        DownloadCenter.refreshFileStatus(fileId, DownloadConst.IN_PROGRESS, target.getPath(), type);

        //将文件分片并分开下载
        long threadCount = threadCount(totalSize);
        //每一个线程分到的任务下载量
        long perThreadSize = totalSize / threadCount;
        residue = threadCount;
        long from, to;
        int partNumber = 0;
        //线程下载任务结果
        List<Future> tasResult = new ArrayList<>(16);
        //断点续传记录
        List<FilePart> fileParts = new ArrayList<>();
        while (totalSize > 0) {
            //计算分片
            to = totalSize;
            from = totalSize - perThreadSize;
            partNumber++;
            if (from < 0) {
                from = 0;
            }
            totalSize -= perThreadSize;

            //开始下载
            Downloader downloader = new Downloader(partNumber, from, to, target, uri);
            Future future = executorServiceConcurrence.submit(downloader);
            tasResult.add(future);

            //记录分片工作
            FilePart filePart = new FilePart();
            filePart.setId(partNumber);
            filePart.setStart(from);
            filePart.setEnd(to);
            filePart.setFilePath(target.getPath());
            filePart.setName(target.getName());
            filePart.setFileId(fileId);
            filePart.setUrl(uri);
            fileParts.add(filePart);
        }

        //保存每个分片工作到本地
        savePartTas(fileParts);
        waitThreadDisposeResult(tasResult, fileParts);

    }

    private boolean unfinishedWork(String json) {
        if (Objects.nonNull(json)) {
            List<Future> tasResult = new ArrayList<>();
            List<FilePart> fileParts = JSON.parseArray(json, FilePart.class, ParserConfig.global);
            for (FilePart fp : fileParts) {
                //未下载完成
                if (!fp.status) {
                    log.info("开始下载未完成资源-文件ID{},分片ID:{}", fileId, fp.getId());
                    //开始下载
                    Downloader downloader = new Downloader(fp.getId(), fp.getStart(), fp.getEnd(), target, uri);

                    Future future = executorServiceConcurrence.submit(downloader);
                    tasResult.add(future);
                }
            }

            //等待线程处理结果
            waitThreadDisposeResult(tasResult, fileParts);
            return true;
        }
        return false;
    }

    /**
     * 等待线程处理结果
     *
     * @param tasResult 线程结果集合
     * @param fileParts 文件分片
     */
    private void waitThreadDisposeResult(List<Future> tasResult, List<FilePart> fileParts) {
        for (Future f : tasResult) {
            try {
                f.get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
            //todo 未处理失败的情况。网络断开导致线程超时
        }

        //更新文件状态为完成
        Long resourceId = DownloadCenter.refreshFileStatus(fileId, DownloadConst.ACCOMPLISH, target.getPath(), type);
        if (null != resourceId) {
            //通知服务器已下载完当前资源
            updateMediaLinkState(resourceId);
        }

        log.info(fileId + "\t下载完成");
        //删除线程分片任务文件
        FileUtil.del(fileParts.get(0).getFilePath() + ".tmp");
        executorServiceConcurrence.shutdown();

        //播放列表为空检测
//        playListNull();
    }

    /**
     * 根据文件总大小计算线程数量
     */
    public long threadCount(long totalSize) {
        //一个分片大小10mb
        long partSize = 20485760;
        long result = totalSize / partSize;
        log.info("文件" + totalSize + "字节\t分片" + result);

        if (result == 0) {
            return 1;
        }

        return result;
    }

    /**
     * 在任务下载完后播放列表为空（未播放视频）将自动播放已下载完成的资源
     */
    private void playListNull() {
        String resourcePath = ResourcePublishing.playListNullChke();
        if (!StrUtil.isEmpty(resourcePath)) {
            VlcjPlayer.play(resourcePath);
        }
    }

    @Override
    public void run() {
        start();
    }
}
